#include "hzpch.h"
#include "Camera.h"

#include "Hazel/Core/Input.h"
#include "Hazel/Core/KeyCodes.h"
#include "Hazel/Core/MouseButtonCodes.h"
#include <glm/ext/quaternion_trigonometric.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtx/quaternion.hpp>
namespace Hazel
{
	Camera::Camera(float verticalFOV, float nearClip, float farClip)
		: m_VerticalFOV(verticalFOV), m_NearClip(nearClip), m_farClip(farClip)
	{
		m_ForwardDirection = glm::vec3(0.0f, 0.0f, -1.0f);
		m_Position = glm::vec3(0.0f, 0.0f, 5.0f);
	}

    bool Camera::OnUpdate(Timestep ts)
    {
        auto currentMousePos = Input::GetMousePosition();
        glm::vec2 mousePos = { currentMousePos.first, currentMousePos.second };
        if(m_LastMousePosition[0] == 0 && m_LastMousePosition[1] == 0)
            m_LastMousePosition = mousePos;

        glm::vec2 delta = (mousePos - m_LastMousePosition) * 0.002f;
        m_LastMousePosition = mousePos;

        if (!Input::IsMouseButtonPressed(HZ_MOUSE_BUTTON_RIGHT))
        {
            Input::SetCursorMode(CursorMode::Normal);
            return false;
        }

        Input::SetCursorMode(CursorMode::Hidden);

        bool moved = false;

        constexpr glm::vec3 upDirection(0.0f, 1.0f, 0.0f);
        glm::vec3 rightDirection = glm::cross(m_ForwardDirection, upDirection);

        float speed = 5.0f;

        // Movement
        if (Input::IsKeyPressed(HZ_KEY_LEFT) || Input::IsKeyPressed(HZ_KEY_A))
        {
            m_Position -= speed * ts * rightDirection;
            moved = true;
        }
        else if (Input::IsKeyPressed(HZ_KEY_RIGHT) || Input::IsKeyPressed(HZ_KEY_D))
        {
            m_Position += speed * ts * rightDirection;
            moved = true;
        }

        if (Input::IsKeyPressed(HZ_KEY_UP) || Input::IsKeyPressed(HZ_KEY_W))
        {
            m_Position += speed * ts * m_ForwardDirection;
            moved = true;
        }
        else if (Input::IsKeyPressed(HZ_KEY_DOWN) || Input::IsKeyPressed(HZ_KEY_S))
        {
            m_Position -= speed * ts * m_ForwardDirection;
            moved = true;
        }

        if (Input::IsKeyPressed(HZ_KEY_Q))
        {
            m_Position += speed * ts * upDirection;
            moved = true;
        }
        else if (Input::IsKeyPressed(HZ_KEY_E))
        {
            m_Position -= speed * ts * upDirection;
            moved = true;
        }

        // Rotation
        if (delta.x != 0.0f || delta.y != 0.0f)
        {
            float pitchDelta = ts * delta.y * GetRotationSpeed();
            float yawDelta = ts * delta.x * GetRotationSpeed();
            glm::quat q = glm::normalize(glm::cross(glm::angleAxis(-pitchDelta, rightDirection), glm::angleAxis(-yawDelta,glm::vec3(0.f, 1.0f, 0.0f))));
            m_ForwardDirection = glm::rotate(q, m_ForwardDirection);
            moved = true;
        }

        if (moved)
        {
            RecalculateView();
            RecalculateRayDirections();
        }

        

        return moved;
    }

	float Camera::GetRotationSpeed()
	{
		return 10.0f;
	}

    void Camera::OnResize(uint32_t width, uint32_t height)
    {
		if (width == m_ViewPortHeight && height == m_ViewPortHeight)
			return;

		m_ViewPortWidth = width;
		m_ViewPortHeight = height;

        //m_LastMousePosition = glm::vec2(0.0f);
		RecalculateProjection();
		RecalculateRayDirections();
    }

	void Camera::RecalculateProjection()
	{
        m_Projection = glm::perspectiveFov(glm::radians(m_VerticalFOV), (float)m_ViewPortWidth, (float)m_ViewPortHeight, m_NearClip, m_farClip);
		m_InverseProjection = glm::inverse(m_Projection);
	}

    void Camera::RecalculateView()
    {
		m_View = glm::lookAt(m_Position, m_Position + m_ForwardDirection, glm::vec3(0.0f, 1.0f, 0.0f));
		m_InverseView = glm::inverse(m_View);
    }

    void Camera::RecalculateRayDirections()
    {
        m_RayDirections.resize(m_ViewPortWidth * m_ViewPortHeight);

        for (uint32_t y = 0; y < m_ViewPortHeight; y++)
        {
            for (uint32_t x = 0; x < m_ViewPortWidth; x++)
            {
				glm::vec2 coord = { (float)x / (float)m_ViewPortWidth, (float)y / (float)m_ViewPortHeight };
				coord = coord * 2.0f - 1.0f;

				glm::vec4 target = m_InverseProjection * glm::vec4(coord.x, coord.y, 1.0f, 1.0f);
                glm::vec3 rayDirection = glm::vec3(m_InverseView * glm::vec4(glm::normalize(glm::vec3(target) / target.w), 0.0f));
				m_RayDirections[x + y * m_ViewPortWidth] = rayDirection;
            }
        }
    }

	

}